
package ueb14.htw.tree;


/**
 *
 * @author adier
 */
public class TreeImpl implements Tree, Comparable<Object> {
    
    private TreeImpl    leftChild      = null;
    private TreeImpl    rightChild     = null;
    private TreeImpl    parent         = null;
    private Object      reference      = null; 
    private String      name           = null;
    private int         size           = 0;
    private boolean     isRed          = false;
    private String      getTreeMax     = null;
    
    
    public TreeImpl() {
    }
    
    public TreeImpl(TreeImpl parent){
        this.parent = parent;
    }
    
    public TreeImpl(String name) throws TreeErrorException {
        setName(name);
    }
    
    
    @Override
    public String getName(){
        return name;
    }
    
    @Override
    public TreeImpl getLeftChild(){
        return leftChild;
    }
    
    @Override
    public TreeImpl getRightChild(){
        return rightChild;
    }
    
    @Override
    public TreeImpl getParent() throws TreeErrorException{
        if(parent == null)
            throw new TreeErrorException("root has no parent");
        return parent;
    }
    
    @Override
    public TreeImpl getGrantParent() throws TreeErrorException{
        return getParent().getParent();
    }
    
    @Override
    public TreeImpl getUncle() throws TreeErrorException {
        if((this.getParent()==null)||(this.getGrantParent()==null))
                return null;
        if(this.getParent().isRightChild())
        {
                if(((this.getGrantParent().getLeftChild() != null)) || ( ! (this.getGrantParent().getLeftChild().isNil())))
                {
                    return this.getGrantParent().getLeftChild();
                }
                else
                {
                    return null;
                }
        }
        else
        {
                if(((this.getGrantParent().getRightChild() != null)) || ( ! (this.getGrantParent().getRightChild().isNil())))
                {
                    return this.getGrantParent().getRightChild();
                }
                else
                {
                    return null;
                }
        }
    }
    
    public TreeImpl getMax() {
        if(!(getRightChild().isNil()))
        {
            return getRightChild().getMax();
        }
        else
        {
            return this;
        }
    }

    public TreeImpl getMin() {
        if(!(getLeftChild().isNil()))
        {
            return getLeftChild().getMin();
        }
        else
        {
            return this;
        }
    }
    
    @Override
    public int getSize(){
        return this.size;
    }

    @Override
    public Object getReference() {
        return reference;
    }
    
    @Override
    public boolean isRed(){
        return isRed;
    }
    
    @Override
    public boolean isBlack(){
        return (!isRed());
    }
    
    @Override
    public void makeRed(){
        this.isRed = true;
    }
    
    @Override
    public void makeBlack(){
        this.isRed = false;
    }

    @Override
    public void setReference(Object reference) {
        this.reference = reference;
    }

    @Override
    public void setName(String name) throws TreeErrorException {
        if(name == null)
            throw new TreeErrorException("You can not set the name null");
        this.name = name;
    }
    
    
    @Override
    public void setLeftChild(TreeImpl leftChild) throws TreeErrorException{
        this.leftChild      = leftChild;
        if(isRoot()){
            getLeftChild().setParent(this);
        } else if(isRightChild()){
            getLeftChild().setParent(this);
        }else {
            getLeftChild().setParent(this);
        }
        leftChild.leftChild              = new TreeImpl();
        leftChild.leftChild.setParent(leftChild);
        leftChild.rightChild             = new TreeImpl();
        leftChild.rightChild.setParent(rightChild);
        leftChild.makeRed();
    }  
    
    public TreeImpl getBrother() throws TreeErrorException {
        if(getParent()==null)
            return null;
        else
        {
            if(this.isRightChild())
            {
                return (this.getParent().getLeftChild());
            }
            else
            {
                return (this.getParent().getRightChild());
            }
        }
    }
    
    @Override
    public boolean isNil() {
        if(getName()==null)
        {
            return true;
        }
        else
        {
            return false;
        }
    }
    
    @Override
    public void setRightChild(TreeImpl rightChild) throws TreeErrorException{
        this.rightChild     = rightChild;
        if((isRoot())){
            getRightChild().setParent(this);
        } else if(isRightChild()){
            getRightChild().setParent(this);
        }else {
            getRightChild().setParent(this);
        }
        rightChild.leftChild         = new TreeImpl();
        rightChild.leftChild.setParent(leftChild);
        rightChild.rightChild        = new TreeImpl();
        rightChild.rightChild.setParent(rightChild);
        rightChild.makeRed();
    }
    
    @Override
    public void setParent(TreeImpl parent) throws TreeErrorException{
        this.parent         = parent;
        setSize();
    }
    
    @Override
    public void setGrantParent(TreeImpl grandParent) throws TreeErrorException{
        getParent().setParent(grandParent);
    }
    
    @Override
    public void setUncle(TreeImpl uncle) throws TreeErrorException{
        if(getParent().isRightChild())
        {
            getGrantParent().setLeftChild(uncle);
        }
        else
        {
            getGrantParent().setLeftChild(uncle);
        }
    }
    
    @Override
    public boolean isRoot(){
        try {
            TreeImpl test = getParent(); // Exception wenn parent null
            return false;

        } catch (TreeErrorException ex) {
            return true;
        }
    }
    
    @Override
    public boolean isRightChild() throws TreeErrorException{
        if(getParent().getRightChild().getRightChild().equals(getRightChild()))
        {
            return true;
        }
        else if (getParent().getLeftChild().getRightChild().equals(getRightChild()))
        {
            return false;
        }
        else
        {
            throw new TreeErrorException("Something went wrong with the references");
        }
    }
    
    @Override
    public boolean isLeftChild() throws TreeErrorException{
        return (! isRightChild());
    }
    
    private TreeImpl getMe() throws TreeErrorException{
        if(getParent().getRightChild().getRightChild().equals(getRightChild()))
        {
            return getParent().getRightChild();
        }
        else if (getParent().getLeftChild().getRightChild().equals(getRightChild()))
        {
            return getParent().getLeftChild();
        }
        else
        {
            throw new TreeErrorException("Something went wrong with the references");
        }
    }
    
    @Override
    public TreeImpl search(String name) throws TreeErrorException{
        if(getName() == null)
        {
            return this;
        }
        if(getName().equals(name)) 
        {
            return this;
        } 
        else if(getName().compareTo(name) > 1)
        {
            return getLeftChild().search(name);
        }
        else 
        {
            return getRightChild().search(name);
        }
        
    }
    
    
    private void setSize() throws TreeErrorException{
        this.size           = addSize(0, parent);
    }
    
    // important to make the size method endrecursiv
    private int addSize(int actualSize,TreeImpl parent) throws TreeErrorException{
        if(parent == null){
            return 0;
        }
        else{
            return addSize(actualSize+1, getGrantParent());
        }
    }

    @Override
    public int compareTo(Object o) {
        return getName().compareTo(((TreeImpl) o).getName());      
    }

    /**
     * checks if two Objects are the same
     * 
     * @param o object to test with
     * @return if it is equals
     */
    @Override
    public boolean equals(Object o) {
        if(getName().equals(( (TreeImpl) o).getName() ) ){
            return true;
        }
        else{
            return false;
        }
    }
    
    @Override
    public String toString() {
        StringBuffer string = new StringBuffer("[");


        if (getName() == null){
            string.append(" null "); // TreeImpl is empty, so leave.
        }

        if (getLeftChild() != null) {
            getLeftChild().toString();
            string.append(", ");
        }

        string.append(getName());

        if (getRightChild() != null) {
            string.append(", ");
            getRightChild().toString();
            string.append("\t");
        }
        
        string.append("]");
        return string.toString();
        
    }
    
}
